#include <string.h>
#include <time.h>
#include "tst/test_frame.h"
#include "tst/bas/test_container_utility.h"
#include "bas/bas_list.h"

static int cpfunc(const void *pvFirst, const void *pvSecond)
{
    if (*(int *)pvFirst < *(int *)pvSecond)
    {
        return -1;
    }
    else if (*(int *)pvFirst > *(int *)pvSecond)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

int tst_basListCreate_simple(void)
{
    T_List *ptList = basListCreate(_T(T_TestSimpleStruct));

    TEST_ASSERT(ptList != NULL);
    TEST_ASSERT(ptList->tTypeInfo.nTypeSize == sizeof(T_TestSimpleStruct));
    TEST_ASSERT(NULL == ptList->tTypeInfo.pfnDestroy);
    TEST_ASSERT(NULL == ptList->tTypeInfo.pfnCopy);
    TEST_ASSERT(NULL == ptList->tTypeInfo.pfnLess);
    TEST_ASSERT(NULL == ptList->tTypeInfo.pfnInit);
    TEST_ASSERT(ptList->ptCallBackTable != NULL);
    TEST_ASSERT(ptList->ptEndNode->ptPrev == ptList->ptEndNode);
    TEST_ASSERT(ptList->ptEndNode->ptNext == ptList->ptEndNode);

    basListDestroy(ptList);
    
    return 0;
}

int tst_basListCreate_complex(void)
{
    T_List *ptList = basListCreate(_T(T_TestComplexStruct,
                                      testComplexStructDestroy, testComplexStructCopy,
                                      testComplexStructLess, testComplexStructInit));

    TEST_ASSERT(ptList != NULL);
    TEST_ASSERT(ptList->tTypeInfo.nTypeSize == sizeof(T_TestComplexStruct));
    TEST_ASSERT((T_DestroyFunction)testComplexStructDestroy == ptList->tTypeInfo.pfnDestroy);
    TEST_ASSERT((T_CopyFunction)testComplexStructCopy == ptList->tTypeInfo.pfnCopy);
    TEST_ASSERT((T_CompareFunction)testComplexStructLess == ptList->tTypeInfo.pfnLess);
    TEST_ASSERT((T_InitFunction)testComplexStructInit == ptList->tTypeInfo.pfnInit);
    TEST_ASSERT(ptList->ptCallBackTable != NULL);
    TEST_ASSERT(ptList->ptEndNode->ptPrev == ptList->ptEndNode);
    TEST_ASSERT(ptList->ptEndNode->ptNext == ptList->ptEndNode);

    basListDestroy(ptList);
    
    return 0;
}

int tst_basListEmpty(void)
{
    T_List *ptList = basListCreate(_T(T_TestSimpleStruct));

    TEST_ASSERT(ptList != NULL);
    TEST_ASSERT(basListEmpty(ptList));

    basListDestroy(ptList);
    
    return 0;
}

int tst_basListPush(void)
{
    T_TestSimpleStruct tTestSimpleStruct;
    T_List *ptList = basListCreate(_T(T_TestSimpleStruct));

    TEST_ASSERT(ptList != NULL);
    
    for (int i = 0; i < 100000; ++i)
    {
        memset(&tTestSimpleStruct, 0, sizeof(T_TestSimpleStruct));
        tTestSimpleStruct.i = i;
        tTestSimpleStruct.f = (float)i;
        TEST_ASSERT(basListPushBack(ptList, &tTestSimpleStruct) == 0);
    }

    TEST_ASSERT(99999 == ((T_TestSimpleStruct *)ptList->ptEndNode->ptPrev->caData)->i);
    TEST_ASSERT(0 == ((T_TestSimpleStruct *)ptList->ptEndNode->ptNext->caData)->i);
    TEST_ASSERT(1 == ((T_TestSimpleStruct *)ptList->ptEndNode->ptNext->ptNext->caData)->i);

    for (int i = 0; i < 100000; ++i)
    {
        memset(&tTestSimpleStruct, 0, sizeof(T_TestSimpleStruct));
        tTestSimpleStruct.i = i;
        tTestSimpleStruct.f = (float)i;
        TEST_ASSERT(basListPushFront(ptList, &tTestSimpleStruct) == 0);
    }
    TEST_ASSERT(99999 == ((T_TestSimpleStruct *)ptList->ptEndNode->ptPrev->caData)->i);
    TEST_ASSERT(99999== ((T_TestSimpleStruct *)ptList->ptEndNode->ptNext->caData)->i);

    basListDestroy(ptList);
        
    return 0;
}

int tst_basListPop(void)
{
    T_TestComplexStruct tTestComplexStruct;
    T_List *ptList = basListCreate(_T(T_TestComplexStruct,
                                      testComplexStructDestroy, testComplexStructCopy,
                                      testComplexStructLess, testComplexStructInit));
    TEST_ASSERT(ptList != NULL);

    for (int j = 0; j < 100; ++j)
    {
        for (int i = 0; i < 10000; ++i)
        {
            testComplexStructInit(&tTestComplexStruct, NULL); 
            TEST_ASSERT(basListPushBack(ptList, &tTestComplexStruct) == 0); 
            testComplexStructDestroy(&tTestComplexStruct);
        }

        for (int i = 0; i < 5000; ++i)
        {
            basListPopBack(ptList);
        }

        for (int i = 0; i < 5000; ++i)
        {
            basListPopFront(ptList);
        }

        TEST_ASSERT(basListEmpty(ptList));
        TEST_ASSERT(basListSize(ptList) == 0);
    }

    basListDestroy(ptList);
    
    return 0;
}

int tst_basListSize(void)
{
    T_TestSimpleStruct tTestSimpleStruct;
    T_List *ptList = basListCreate(_T(T_TestSimpleStruct));

    TEST_ASSERT(ptList != NULL);
    
    for (int i = 0; i < 10000; ++i)
    {
        memset(&tTestSimpleStruct, 0, sizeof(T_TestSimpleStruct));
        tTestSimpleStruct.i = i;
        tTestSimpleStruct.f = (float)i;
        TEST_ASSERT(basListPushBack(ptList, &tTestSimpleStruct) == 0);
        TEST_ASSERT(basListSize(ptList) == i + 1);
    }

    basListDestroy(ptList);

    return 0;
}

int tst_basListResize(void)
{
    T_List *ptList = basListCreate(_T(T_TestComplexStruct,
                                      testComplexStructDestroy, testComplexStructCopy,
                                      testComplexStructLess, testComplexStructInit));

    TEST_ASSERT(ptList != NULL);

    for (int i = 0; i < 100; ++i)
    {
        basListResize(ptList, 20000);
        basListResize(ptList, 0);
        TEST_ASSERT(basListEmpty(ptList));
    }

    basListDestroy(ptList);
     
    return 0;
}

int tst_basListDestroy(void)
{
    T_TestSimpleStruct tTestSimpleStruct;
    T_List *ptList = NULL;
    
    for (int j = 0; j < 100; ++j)
    {
        ptList = basListCreate(_T(T_TestSimpleStruct));
        TEST_ASSERT(ptList != NULL);
        for (int i = 0; i < 10000; ++i)
        {
            memset(&tTestSimpleStruct, 0, sizeof(T_TestSimpleStruct));
            tTestSimpleStruct.i = i;
            tTestSimpleStruct.f = (float)i;
            TEST_ASSERT(basListPushBack(ptList, &tTestSimpleStruct) == 0);
        }
        basListDestroy(ptList);
    }

    return 0;
}

int tst_basListIterator(void)
{
    T_TestSimpleStruct tTestSimpleStruct;
    T_List *ptList = basListCreate(_T(T_TestSimpleStruct));

    TEST_ASSERT(ptList != NULL);
    
    for (int i = 0; i < 100000; ++i)
    {
        memset(&tTestSimpleStruct, 0, sizeof(T_TestSimpleStruct));
        tTestSimpleStruct.i = i;
        tTestSimpleStruct.f = (float)i;
        TEST_ASSERT(basListPushBack(ptList, &tTestSimpleStruct) == 0);
    }

    int iCount = 0;

    //正向迭代
    for (T_Iterator tIterator = basListBegin(ptList);
         !basIteratorEqual(tIterator, basListEnd(ptList));
         tIterator = basIteratorNext(tIterator))
    {
        TEST_ASSERT(BAS_ITER_GET_FIELD(tIterator, T_TestSimpleStruct, i) == iCount++);
    }

    //反向迭代
    for (T_Iterator tIterator = basListRBegin(ptList);
         !basIteratorEqual(tIterator, basListREnd(ptList));
         tIterator = basIteratorPrev(tIterator))
    {
        TEST_ASSERT(BAS_ITER_GET_FIELD(tIterator, T_TestSimpleStruct, i) == --iCount);
    }

    basListDestroy(ptList);

    return 0;
}

int tst_basListInsert(void)
{
    T_TestSimpleStruct tTestSimpleStruct;
    T_List *ptList = basListCreate(_T(T_TestSimpleStruct));

    TEST_ASSERT(ptList != NULL);
    
    for (int i = 0; i < 100000; ++i)
    {
        memset(&tTestSimpleStruct, 0, sizeof(T_TestSimpleStruct));
        tTestSimpleStruct.i = i;
        tTestSimpleStruct.f = (float)i;
        TEST_ASSERT(basIteratorIsValid(basListInsert(ptList, basListEnd(ptList), &tTestSimpleStruct)));
    }
    TEST_ASSERT(basListSize(ptList) == 100000);
    basListResize(ptList, 0);
    TEST_ASSERT(basListEmpty(ptList));

    basListDestroy(ptList);
    
    return 0;
}

int tst_basListErase(void)
{
    T_TestSimpleStruct tTestSimpleStruct;
    T_List *ptList = basListCreate(_T(T_TestSimpleStruct));

    TEST_ASSERT(ptList != NULL);

    for (int j = 0; j < 10; ++j)
    {
        for (int i = 0; i < 100000; ++i)
        {
            memset(&tTestSimpleStruct, 0, sizeof(T_TestSimpleStruct));
            tTestSimpleStruct.i = i;
            tTestSimpleStruct.f = (float)i;
            TEST_ASSERT(basIteratorIsValid(basListInsert(ptList, basListEnd(ptList), &tTestSimpleStruct)));
        }

        for (int i = 0; i < 100000; ++i)
        {
            basListErase(ptList, basListBegin(ptList));
        }

        basListResize(ptList, 20000);

        for (int i = 0; i < 20000; ++i)
        {
            basListErase(ptList, basListBegin(ptList));
        }
        TEST_ASSERT(basListEmpty(ptList));

    }

    basListDestroy(ptList);

    return 0;
}

int tst_basList_buildin_type(void)
{
    T_List *ptList = NULL;

    TEST_ASSERT((ptList = basListCreate(_T(BUILDIN_STRING))) != NULL);
    for (int i = 0; i < 100; ++i)
    {
        for (int j = 0; j < 10000; ++j)
        {
            TEST_ASSERT(basListPushBack(ptList, "Hello,World!") == 0);
        }
        for (T_Iterator tIterator = basListBegin(ptList);
             !basIteratorEqual(tIterator, basListEnd(ptList));
             tIterator = basIteratorNext(tIterator))
        {
            TEST_ASSERT(strcmp("Hello,World!", *(char **)basIteratorGetPointer(tIterator)) == 0);
        }
        basListClear(ptList);

        for (int j = 0; j < 10000; ++j)
        {
            TEST_ASSERT(basIteratorIsValid(basListInsert(ptList, basListEnd(ptList), "Holy!")));
        }
        for (T_Iterator tIterator = basListBegin(ptList);
             !basIteratorEqual(tIterator, basListEnd(ptList));
             tIterator = basIteratorNext(tIterator))
        {
            TEST_ASSERT(strcmp("Holy!", *(char **)basIteratorGetPointer(tIterator)) == 0);
        }
        basListClear(ptList);
        TEST_ASSERT(basListEmpty(ptList));
    }
    basListDestroy(ptList);

    TEST_ASSERT((ptList = basListCreate(_T(BUILDIN_INT))) != NULL);
    for (int i = 0; i < 100; ++i)
    {
        for (int j = 0; j < 10000; ++j)
        {
            TEST_ASSERT(basListPushBack(ptList, 123) == 0)
        }
        for (T_Iterator tIterator = basListBegin(ptList);
             !basIteratorEqual(tIterator, basListEnd(ptList));
             tIterator = basIteratorNext(tIterator))
        {
            TEST_ASSERT(123 == *(int *)basIteratorGetPointer(tIterator));
        }
        basListResize(ptList, 0);
        TEST_ASSERT(basListEmpty(ptList));
    }
    basListDestroy(ptList);

    return 0;
}

int tst_basListSort(void)
{
    T_List *ptList = NULL;
    int a[1000000];
    T_Iterator tIterator;
    int i = 0;

    srand(time(NULL));

    TEST_ASSERT((ptList = basListCreate(_T(BUILDIN_INT))) != NULL);

    for (i = 0; i < 1000000; ++i)
    {
        a[i] = rand();
    }

    for (i = 0; i < 1000000; ++i)
    {
        TEST_ASSERT(basListPushBack(ptList, a[i]) == 0);
    }
    
    qsort(a, 1000000, sizeof(int), cpfunc);
    basListSort(ptList);

    for (tIterator = basListBegin(ptList), i = 0;
         !basIteratorEqual(tIterator, basListEnd(ptList));
         tIterator = basIteratorNext(tIterator))
    {
        TEST_ASSERT((*(int *)basIteratorGetPointer(tIterator)) == a[i++]);
    }

    basListDestroy(ptList);

    return 0;
}
